package com.ruoyi.video.server.nvr;

import com.ruoyi.common.utils.StringUtils;
import com.ruoyi.common.utils.spring.SpringUtils;
import com.ruoyi.video.domain.VideoCamera;
import com.ruoyi.video.domain.VideoNvr;
import com.ruoyi.video.server.push.PushMessage;
import com.ruoyi.video.server.push.PushServer;
import com.ruoyi.video.server.rtsp.RtspServer;
import com.ruoyi.video.server.utils.StreamUtils;
import com.ruoyi.video.service.IVideoCameraService;
import com.ruoyi.video.service.IVideoNvrService;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.io.IOUtils;
import org.bytedeco.javacv.FFmpegFrameGrabber;
import org.bytedeco.javacv.FFmpegFrameRecorder;

import java.io.ByteArrayOutputStream;
import java.net.InetAddress;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

/**
 * 测试NVR各通道连接的摄像机是否拉流正常
 */
@Slf4j
public class NvrTestThread extends Thread{
    private IVideoCameraService videoCameraService;
    private IVideoNvrService videoNvrService;
    private PushServer pushServer;
    private Map<String,VideoCamera> videoCameraMap = new HashMap<>();
    public static boolean running = false;

    /**
     * 初始化
     */
    public void init(){

        videoNvrService = SpringUtils.getBean(IVideoNvrService.class);
        videoCameraService = SpringUtils.getBean(IVideoCameraService.class);
        pushServer = SpringUtils.getBean(PushServer.class);
    }

    @Override
    public void run() {
        if(running){
            log.info("上个NVR检测进程未结束，本次任务将忽略");
            return;
        }
        log.info("开始NVR检测进程...");
        running = true;

        init();

        //再从所有的NVR中拉流测试
        VideoNvr vnc = new VideoNvr();
        vnc.setStatus("1");
        List<VideoNvr> nvrList = videoNvrService.selectVideoNvrList(vnc);
        if(StringUtils.isEmpty(nvrList)){
            running = false;
            log.info("NVR检测进程结束");
            return;
        }
        //先查询已经接入的摄像机
        VideoCamera vcc = new VideoCamera();
        vcc.setType(NvrServer.SERVER_CODE);
        List<VideoCamera> list = videoCameraService.selectVideoCameraList(vcc);
        if(StringUtils.isNotEmpty(list)){
            videoCameraMap = list.stream().collect(Collectors.toMap(VideoCamera::getRtsp, VideoCamera -> VideoCamera));
        }

        for (VideoNvr nvr : nvrList) {
            try {
                testNVR(nvr);
            }catch (Exception e){
                e.printStackTrace();
            }
        }
        videoCameraMap.clear();
        running = false;
        log.info("NVR检测进程结束");
    }

    /**
     * 测试所的的NVR
     */
    public void testNVR(VideoNvr nvr) {
        //先判断与NVR是否网络互联
        try {
            InetAddress inetAddress = InetAddress.getByName(nvr.getIp());
            if(inetAddress.isReachable(3000)){
                if(nvr.getOnline() == null || nvr.getOnline().equalsIgnoreCase("N")){
                    nvr.setOnline("Y");
                    videoNvrService.updateVideoNvr(nvr);
                }
            }else{
                if(nvr.getOnline() == null || nvr.getOnline().equalsIgnoreCase("Y")){
                    nvr.setOnline("N");
                    videoNvrService.updateVideoNvr(nvr);

                    nvrOffLine(nvr);
                    return;
                }
            }
        }catch (Exception e){
            e.printStackTrace();
            if(nvr.getOnline() == null || nvr.getOnline().equalsIgnoreCase("Y")){
                nvr.setOnline("N");
                videoNvrService.updateVideoNvr(nvr);

                nvrOffLine(nvr);
                return;
            }
            return;
        }

        //逐个通道测试
        String rtspTpl = nvr.getRtsp();
        for(int i=0;i<nvr.getChannels();i++){
            String rtsp = rtspTpl.replace("{channel}", i+"");
            try{
                boolean ol = StreamUtils.test(rtsp);
                VideoCamera videoCamera = videoCameraMap.get(rtsp);
                if(videoCamera != null){
                    if(ol && videoCamera.getOnline().equalsIgnoreCase("N")){
                        //离线转在线
                        videoCamera.setOnline("Y");
                        videoCameraService.updateVideoCamera(videoCamera);
                        pushServer.push(PushMessage.DEVICE_ONLINE, videoCamera.getId(), videoCamera);
                    }else if(!ol && videoCamera.getOnline().equalsIgnoreCase("Y")){
                        //在线转离线
                        videoCamera.setOnline("N");
                        videoCameraService.updateVideoCamera(videoCamera);
                        pushServer.push(PushMessage.DEVICE_OFFLINE, videoCamera.getId(), videoCamera);
                    }
                }else if(ol){
                    //发现新设备
                    videoCamera = new VideoCamera();
                    videoCamera.setType(NvrServer.SERVER_CODE);
                    videoCamera.setName(nvr.getName()+i+"通道");
                    videoCamera.setCode(nvr.getId()+"_"+String.format("%03d", i));
                    videoCamera.setRtsp(rtsp);
                    videoCamera.setOnline("Y");
                    videoCamera.setStatus("1");
                    videoCamera.setGroupId(1L);
                    videoCamera.setCreateBy("onvif");
                    videoCameraService.insertVideoCamera(videoCamera);
                }
            }catch (Exception e){
                e.printStackTrace();
                log.error("添加NVR摄像机失败:{}", e);
            }
        }
    }

    /**
     * OVR离线后，所有设备都设置为离线
     * @param nvr
     */
    public void nvrOffLine(VideoNvr nvr){

        String rtspTpl = nvr.getRtsp();
        for(int i=0;i<nvr.getChannels();i++) {
            String rtsp = rtspTpl.replace("{channel}", i+"");
            VideoCamera videoCamera = videoCameraMap.get(rtsp);
            if(videoCamera != null && videoCamera.getOnline().equalsIgnoreCase("Y")){

                //在线转离线
                videoCamera.setOnline("N");
                videoCameraService.updateVideoCamera(videoCamera);
                pushServer.push(PushMessage.DEVICE_OFFLINE, videoCamera.getId(), videoCamera);
            }
        }
    }
}
